﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;
using System.Windows.Controls;

namespace ColorPane
{
    public class PixelHelper : FrameworkElement
    {
        public static readonly DependencyProperty DragLockProperty=
            DependencyProperty.Register("DragLock",typeof(bool),typeof(PixelHelper),new PropertyMetadata(false));

        public static readonly DependencyProperty XProperty
            =DependencyProperty.Register("X",typeof(double),typeof(PixelHelper),new PropertyMetadata(0d));

        public static readonly DependencyProperty YProperty
            =DependencyProperty.Register("Y",typeof(double),typeof(PixelHelper),new PropertyMetadata(0d));

        FrameworkElement c;
        FrameworkElement t;
        bool    _allowXDrag;
        public PixelHelper(FrameworkElement control, FrameworkElement cursor, bool allowX)
        {
            control.MouseLeftButtonDown += LeftButtonDown;
            control.MouseLeftButtonUp += LeftButtonUp;
            //control.LostFocus += LostFocus;
            control.MouseMove += MouseMove;
            // control.MouseUp += LeftButtonUp;
            c = control;
            t = cursor;
            _allowXDrag = allowX;
        }

        internal void LeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            DragLock = true;
            var p = e.GetPosition( (IInputElement) e.Source );
            c.CaptureMouse();
            X = p.X / c.ActualWidth;
            Y = p.Y / c.ActualHeight;
            if ( p.X >= 0 && p.X <= c.ActualWidth && p.Y >= 0 && p.Y <= c.ActualHeight )
            {
                t.Margin = new Thickness( _allowXDrag ? p.X : 0, p.Y, 0, 0 );
                OnCanDragMove?.Invoke( X, Y );
            }
            
        }
        internal void LeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            DragLock = false;
            c.ReleaseMouseCapture();
        }
        internal new void LostFocus(object sender, RoutedEventArgs e)
        {
            DragLock = false;
        }
        internal new void MouseMove(object sender, System.Windows.Input.MouseEventArgs e)
        {
            if ( DragLock )
            {
                var p = e.GetPosition( (IInputElement) e.Source );
                X = p.X / c.ActualWidth;
                Y = p.Y / c.ActualHeight;
                t.IsHitTestVisible = true;
                if ( p.X >= 0 && p.X <= c.ActualWidth && p.Y >= 0 && p.Y <= c.ActualHeight )
                {
                    System.Diagnostics.Debug.Print( $"X:{X}\tY:{Y}" );
                    OnCanDragMove( X, Y );
                    t.Margin = new Thickness( _allowXDrag ? p.X : 0, p.Y, 0, 0 );
                }
            }
        }

        public double X {
            get => (double) this.GetValue( XProperty );
            set => this.SetValue( XProperty, value );
        }

        public double Y {
            get => (double) this.GetValue( YProperty );
            set => this.SetValue( YProperty, value );
        }

        public bool DragLock {
            get => (bool) this.GetValue( DragLockProperty );
            set => this.SetValue( DragLockProperty, value );
        }
        public event Action<double,double> OnCanDragMove;


        public static double GetHSLPaneHue(FrameworkElement hue, double y)
        {
            return ( y / hue.ActualHeight ) * 360;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="hue">0.0~1.0</param>
        /// <param name="saturation">0~1.0</param>
        /// <param name="light">0~1.0</param>
        /// <returns></returns>
        public static SolidColorBrush HSL2RGB(double hue, double saturation, double light)
        {
            hue = hue == 360d ? 259 : hue;
            var q = light < .5f ? ( light * ( 1 + saturation ) ) : ( light + saturation - ( light * saturation ) );
            var p = 2 * light - q;
            var hk = hue / 360d;
            var t = new double[3] { hk + .33d, hk, hk - .33d };
            for ( int i = 0; i < 3; i++ )
                t[i] = t[i] > 1 ? t[i] -= 1.0d : ( t[i] < 0 ? t[i] += 1.0 : t[i] );
            var color = new double[3] { 0, 0, 0 };
            for ( int i = 0; i < 3; i++ )
            {
                var c = color[i];
                if ( t[i] < 0.166d )
                {
                    color[i] = p + ( ( q - p ) * 6 * t[i] );
                }
                else if ( t[i] >= .166d && t[i] < .5d )
                {
                    color[i] = q;
                }
                else if ( t[i] >= .5d && t[i] < .667d )
                {
                    color[i] = p + ( ( q - p ) * 6 * ( .667d - t[i] ) );
                }
                else
                {
                    color[i] = p;
                }               
            }
            System.Diagnostics.Debug.Print( $"R:{(byte) ( color[0] * 255 )}\tG:{(byte) ( color[1] * 255 )}\tB:{(byte) ( color[2] * 255 )}" );
            var brush = new SolidColorBrush( Color.FromRgb( (byte) (255*color[0]), (byte)( color[1]*255), (byte) (color[2]*255) ) );
            return brush;
        }

        public static Tuple<double, double, double> RGB2HSL(double r, double g, double b)
        {
            var max = r > g ? ( r > b ? r : b ) : ( g > b ? g : b );
            var min = r < g ? ( r < b ? r : b ) : ( g < b ? g : b );
            var hue = 0d;
            var cl = max - min;
            if ( max == min )
                hue = 0d;
            else if ( max == r && g >= b )
                hue = 60d * ( g - b ) * cl;
            else if ( max == r && g < b )
                hue = 60d * ( g - b ) * cl + 360;
            else if ( max == g )
                hue = 60d * ( b - r ) * cl + 120d;
            else if ( max == b )
                hue = 60d * ( r - g ) * cl + 240d;
            var l = 0.5d * ( min + max );
            var s = 0d;
            if ( l == 0 || max == min )
                s = 0d;
            else if ( 0 < l && l >= .5d )
                s = cl / ( 2 * l );
            else if ( l > .5d )
                s = cl / ( 2 - 2 * l );
            return new Tuple<double, double, double>( hue, s, l );
        }

        public static SolidColorBrush HSV2RGB(double hue, double saturation, double value)
        {
            var hi = 0;
            hi = (int) ( hue / 60 ) % 6;
            var f = hue / 60d - hi;
            var p = value * ( 1 - saturation );
            var q = value * ( 1 - f * saturation );
            var t = value * ( 1 - ( 1 - f ) * saturation );
            Color c = default( Color );
            switch ( hi )
            {
                case 0:
                    c = Color.FromRgb( (byte) ( value * 255 ), (byte) ( t * 255 ), (byte) ( p * 255 ) );
                    return new SolidColorBrush( c );
                case 1:
                    c = Color.FromRgb( (byte) ( q * 255 ), (byte) ( value * 255 ), (byte) ( p * 255 ) );
                    return new SolidColorBrush( c );
                case 2:
                    c = Color.FromRgb( (byte) ( p * 255 ), (byte) ( value * 255 ), (byte) ( t * 255 ) );
                    return new SolidColorBrush( c );
                case 3:
                    c = Color.FromRgb( (byte) ( p * 255 ), (byte) ( q * 255 ), (byte) ( value * 255 ) );
                    return new SolidColorBrush( c );
                case 4:
                    c = Color.FromRgb( (byte) ( t * 255 ), (byte) ( p * 255 ), (byte) ( value * 255 ) );
                    return new SolidColorBrush( c );
                case 5:
                    c = Color.FromRgb( (byte) ( value * 255 ), (byte) ( p * 255 ), (byte) ( q * 255 ) );
                    return new SolidColorBrush( c );
                default:
                    c = Color.FromRgb( 255, 255, 255 );
                    return new SolidColorBrush( c );
            }
        }

        public static Tuple<double, double, double> RGB2HSV(double r, double g, double b)
        {
            var max = r > g ? ( r > b ? r : b ) : ( g > b ? g : b );
            var min = r < g ? ( r < b ? r : b ) : ( g < b ? g : b );
            var hue = 0d;
            var cl = max - min;
            if ( max == min )
                hue = 0d;
            else if ( max == r && g >= b )
                hue = 60d * ( g - b ) * cl;
            else if ( max == r && g < b )
                hue = 60d * ( g - b ) * cl + 360;
            else if ( max == g )
                hue = 60d * ( b - r ) * cl + 120d;
            else if ( max == b )
                hue = 60d * ( r - g ) * cl + 240d;

            var s = 0d;
            if ( max != 0 )
                s = 1 - min / max;
            var v = max;
            return new Tuple<double, double, double>( hue, s, v );
        }

        public static double GetThickness(FrameworkElement control,double thickness)
        {
            return thickness * control.ActualWidth / 2;
        }
    }
}
